package com.igoal.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.tuckey.web.filters.urlrewrite.utils.StringUtils;

import com.igoal.dao.NewsDAO;
import com.igoal.dao.TagDAO;
import com.igoal.dao.UserDAOExt;
import com.igoal.dao.UserTagDAO;
import com.igoal.dto.NewsDTO;
import com.igoal.dto.TagDTO;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.News;
import com.igoal.entity.Tag;
import com.igoal.entity.Taggable;
import com.igoal.entity.User;
import com.igoal.entity.UserTag;
import com.igoal.entity.factory.EntityFactory;
import com.igoal.web.Constants;

@Service("tagService")
public class TagServiceImpl implements TagService {
	private static final Log logger = LogFactory.getLog(TagServiceImpl.class);
	
	private TagDAO tagDAO;
	private UserTagDAO userTagDAO;
	private NewsDAO newsDAO;
	@Qualifier("userDAOExt")
	private UserDAOExt userDAO;

	@Autowired
	public void setUserDAO(UserDAOExt userDAO) {
		this.userDAO = userDAO;
	}

	@Autowired
	public void setTagDAO(TagDAO tagDAO) {
		this.tagDAO = tagDAO;
	}

	@Autowired
	public void setUserTagDAO(UserTagDAO userTagDAO) {
		this.userTagDAO = userTagDAO;
	}

	@Autowired
	public void setNewsDAO(NewsDAO newsDAO) {
		this.newsDAO = newsDAO;
	}

	public List<TagDTO> listAll() {
		List<Tag> tags = tagDAO.getAllDistinct();
		List<TagDTO> result = new ArrayList<TagDTO>();
		for(Tag tag : tags){
			TagDTO dto = new TagDTO();
			dto.initFrom(tag);
			result.add(dto);
		}
		
		return result;
	}

	public int addTag(String tagName) {
		if(StringUtils.isBlank(tagName)){
			return SaveResult.FAIL;
		}
		
		tagName = tagName.trim().toLowerCase();
		if(tagDAO.exists(tagName)){
			return SaveResult.EXIST;
		}
		
		Tag tag = EntityFactory.createTag(tagName);
		try{
			tagDAO.save(tag);
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
		return SaveResult.SUCCESS;
	}
	
	public int removeAllTag(ComponentEnum component, Long componentId){
		try{
			userTagDAO.deleteAllTag(componentId, component);
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
		return SaveResult.SUCCESS;
	}

	public int tagComponent(String username, Long tagId, String tagName,
			ComponentEnum component, Long componentId) {
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			Tag tag = findTag(tagId, tagName);
			if(tag == null){
				if(!StringUtils.isBlank(tagName)){
					addTag(tagName);
					tag = findTag(null, tagName);					
				}else{
					return SaveResult.FAIL;
				}
			}
			UserTag userTag = EntityFactory.createUserTag(tag, user);
			if(!userTagDAO.isComponentTagged(tag.getId(), component, componentId)){
				userTag.setComponent(component);
				userTag.setComponentId(componentId);
				userTagDAO.save(userTag);
			}else{
				return SaveResult.FAIL;
			}
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
		return SaveResult.SUCCESS;
	}

	public int removeTagComponent(String username, Long tagId, ComponentEnum component, Long componentId) {
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserTag userTag = userTagDAO.findByInformation(user.getId(), tagId, component, componentId);
			if(userTag != null){
				userTagDAO.remove(userTag.getId());
				return SaveResult.SUCCESS;
			}
		}catch (Exception e) {
			logger.error(e);			
		}
		return SaveResult.FAIL;
	}

	public List<UserTag> listTaggedComponent(Long tagId, String tagName,
			ComponentEnum component, Date lastComponentCreatedDate, boolean before) {
		Map<String, Object> criteria = new HashMap<String, Object>();
		Tag tag = findTag(tagId, tagName);
		if(tag != null){
			criteria.put("tag", tag);
		}
		
		criteria.put("component", component);
		List<UserTag> taggedComponent = userTagDAO.pagingDependOnOrderProperty(criteria, "createDate", lastComponentCreatedDate, before, Constants.MAX_NEWS_PER_PAGE);
		return taggedComponent;
	}
	
	Tag findTag(Long id, String tagName){
		Tag tag = null;
		if(id != null){
			try{
				tag = tagDAO.get(id);
			}catch (Exception e) {
				logger.error(e);
			}
		}
		
		if(tag == null && !StringUtils.isBlank(tagName)){
			tagName = tagName.trim().toLowerCase();
			tag = tagDAO.findByName(tagName);
		}
		
		return tag;
	}

	public List<NewsDTO> listTaggedNews(Long tagId, String tagName,
			Date lastComponentCreatedDate, boolean before) {
		List<UserTag> taggedComponents = listTaggedComponent(tagId, tagName, ComponentEnum.NEWS, lastComponentCreatedDate, before);
		List<NewsDTO> result = new ArrayList<NewsDTO>();
		for(UserTag userTag : taggedComponents){
			News news = newsDAO.get(userTag.getComponentId());
			NewsDTO dto = new NewsDTO();
			dto.initFrom(news);
			initTags(dto, userTag.getComponentId(), ComponentEnum.NEWS);
			result.add(dto);
		}
		
		return result;
	}
	
	public void initTags(Taggable taggable, Long componentId, ComponentEnum component){
		List<UserTag> taggedComponents = userTagDAO.getTagsOfComponent(componentId, component);
		List<TagDTO> tags = new ArrayList<TagDTO>();		
		for(UserTag userTag : taggedComponents){
			TagDTO tag = new TagDTO();
			tag.initFrom(userTag.getTag());
			tags.add(tag);
		}
		taggable.setTags(tags);
	}

	public int tagComponent(String username, Long[] tagIds, String[] tagNames, ComponentEnum component, Long componentId) {
		if(tagIds != null && tagIds.length > 0){
			return tagComponent(username, tagIds, component, componentId);
		}else if(tagNames != null && tagNames.length > 0){
			return tagComponent(username, tagNames, component, componentId);
		}
		
		return SaveResult.FAIL;
	}
	
	public int tagComponent(String username, Long[] tagIds, ComponentEnum component, Long componentId) {
		try{
			for(Long tagId : tagIds){
				tagComponent(username, tagId, null, component, componentId);
			}
			return SaveResult.SUCCESS;
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}
	
	public int tagComponent(String username, String[] tagNames, ComponentEnum component, Long componentId) {
		try{
			for(String tagName : tagNames){
				tagComponent(username, null, tagName, component, componentId);
			}
			return SaveResult.SUCCESS;
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

}
